/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *//*** File: ptthread.c** Descritpion: Implemenation for threds using pthreds** Exports: ptthread.h*/#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)#include"prlog.h"#include"primpl.h"#include"prpdce.h"#include<pthread.h>#include<unistd.h>#include<string.h>#include<signal.h>#include<dlfcn.h>#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)#include<pthread_np.h>#endif#ifdef SYMBIAN/* In Open C sched_get_priority_min/max do not work properly, so we undefine * _POSIX_THREAD_PRIORITY_SCHEDULING here. */#undef _POSIX_THREAD_PRIORITY_SCHEDULING#endif#ifdef _PR_NICE_PRIORITY_SCHEDULING#undef _POSIX_THREAD_PRIORITY_SCHEDULING#include<sys/resource.h>#ifndef HAVE_GETTID#define gettid() (syscall(SYS_gettid))#endif#endif/* * Record whether or not we have the privilege to set the scheduling * policy and priority of threads. 0 means that privilege is available. * EPERM means that privilege is not available. */staticPRIntnpt_schedpriv=0;externPRLock*_pr_sleeplock;staticstruct_PT_Bookeeping{PRLock*ml;/* a lock to protect ourselves */PRCondVar*cv;/* used to signal global things */PRInt32system,user;/* a count of the two different types */PRUintnthis_many;/* number of threads allowed for exit */pthread_key_tkey;/* thread private data key */PRBoolkeyCreated;/* whether 'key' should be deleted */PRThread*first,*last;/* list of threads we know about */#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0PRInt32minPrio,maxPrio;/* range of scheduling priorities */#endif}pt_book={0};staticvoid_pt_thread_death(void*arg);staticvoid_pt_thread_death_internal(void*arg,PRBoolcallDestructors);staticvoidinit_pthread_gc_support(void);#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0staticPRIntnpt_PriorityMap(PRThreadPrioritypri){#ifdef NTO/* This priority algorithm causes lots of problems on Neutrino * for now I have just hard coded everything to run at priority 10 * until I can come up with a new algorithm. * Jerry.Kirk@Nexwarecorp.com */return10;#elsereturnpt_book.minPrio+pri*(pt_book.maxPrio-pt_book.minPrio)/PR_PRIORITY_LAST;#endif}#elif defined(_PR_NICE_PRIORITY_SCHEDULING)/* * This functions maps higher priorities to lower nice values relative to the * nice value specified in the |nice| parameter. The corresponding relative * adjustments are: * * PR_PRIORITY_LOW +1 * PR_PRIORITY_NORMAL 0 * PR_PRIORITY_HIGH -1 * PR_PRIORITY_URGENT -2 */staticintpt_RelativePriority(intnice,PRThreadPrioritypri){returnnice+(1-pri);}#endif/*** Initialize a stack for a native pthread thread*/staticvoid_PR_InitializeStack(PRThreadStack*ts){if(ts&&(ts->stackTop==0)){ts->allocBase=(char*)&ts;ts->allocSize=ts->stackSize;/* ** Setup stackTop and stackBottom values. */#ifdef HAVE_STACK_GROWING_UPts->stackBottom=ts->allocBase+ts->stackSize;ts->stackTop=ts->allocBase;#elsets->stackTop=ts->allocBase;ts->stackBottom=ts->allocBase-ts->stackSize;#endif}}staticvoid*_pt_root(void*arg){PRIntnrv;PRThread*thred=(PRThread*)arg;PRBooldetached=(thred->state&PT_THREAD_DETACHED)?PR_TRUE:PR_FALSE;pthread_tid=pthread_self();#ifdef _PR_NICE_PRIORITY_SCHEDULINGpid_ttid;#endif#ifdef _PR_NICE_PRIORITY_SCHEDULING/* * We need to know the kernel thread ID of each thread in order to * set its nice value hence we do it here instead of at creation time. */tid=gettid();errno=0;rv=getpriority(PRIO_PROCESS,0);/* If we cannot read the main thread's nice value don't try to change the * new thread's nice value. */if(errno==0){setpriority(PRIO_PROCESS,tid,pt_RelativePriority(rv,thred->priority));}#endif/* ** DCE Threads can't detach during creation, so do it late. ** I would like to do it only here, but that doesn't seem ** to work. */#if defined(_PR_DCETHREADS)if(detached){/* pthread_detach() modifies its argument, so we must pass a copy */pthread_tself=id;rv=pthread_detach(&self);PR_ASSERT(0==rv);}#endif /* defined(_PR_DCETHREADS) *//* Set up the thread stack information */_PR_InitializeStack(thred->stack);/* * Set within the current thread the pointer to our object. * This object will be deleted when the thread termintates, * whether in a join or detached (see _PR_InitThreads()). */rv=pthread_setspecific(pt_book.key,thred);PR_ASSERT(0==rv);/* make the thread visible to the rest of the runtime */PR_Lock(pt_book.ml);/* * Both the parent thread and this new thread set thred->id. * The new thread must ensure that thred->id is set before * it executes its startFunc. The parent thread must ensure * that thred->id is set before PR_CreateThread() returns. * Both threads set thred->id while holding pt_book.ml and * use thred->idSet to ensure thred->id is written only once. */if(!thred->idSet){thred->id=id;thred->idSet=PR_TRUE;}else{PR_ASSERT(pthread_equal(thred->id,id));}#ifdef _PR_NICE_PRIORITY_SCHEDULINGthred->tid=tid;PR_NotifyAllCondVar(pt_book.cv);#endif/* If this is a GCABLE thread, set its state appropriately */if(thred->suspend&PT_THREAD_SETGCABLE)thred->state|=PT_THREAD_GCABLE;thred->suspend=0;thred->prev=pt_book.last;if(pt_book.last)pt_book.last->next=thred;elsept_book.first=thred;thred->next=NULL;pt_book.last=thred;PR_Unlock(pt_book.ml);thred->startFunc(thred->arg);/* make visible to the client *//* unhook the thread from the runtime */PR_Lock(pt_book.ml);/* * At this moment, PR_CreateThread() may not have set thred->id yet. * It is safe for a detached thread to free thred only after * PR_CreateThread() has accessed thred->id and thred->idSet. */if(detached){while(!thred->okToDelete)PR_WaitCondVar(pt_book.cv,PR_INTERVAL_NO_TIMEOUT);}if(thred->state&PT_THREAD_SYSTEM)pt_book.system-=1;elseif(--pt_book.user==pt_book.this_many)PR_NotifyAllCondVar(pt_book.cv);if(NULL==thred->prev)pt_book.first=thred->next;elsethred->prev->next=thred->next;if(NULL==thred->next)pt_book.last=thred->prev;elsethred->next->prev=thred->prev;PR_Unlock(pt_book.ml);/* * Here we set the pthread's backpointer to the PRThread to NULL. * Otherwise the destructor would get called eagerly as the thread * returns to the pthread runtime. The joining thread would them be * the proud possessor of a dangling reference. However, this is the * last chance to delete the object if the thread is detached, so * just let the destructor do the work. */if(PR_FALSE==detached){/* Call TPD destructors on this thread. */_PR_DestroyThreadPrivate(thred);rv=pthread_setspecific(pt_book.key,NULL);PR_ASSERT(0==rv);}returnNULL;}/* _pt_root */staticPRThread*pt_AttachThread(void){PRThread*thred=NULL;/* * NSPR must have been initialized when PR_AttachThread is called. * We cannot have PR_AttachThread call implicit initialization * because if multiple threads call PR_AttachThread simultaneously, * NSPR may be initialized more than once. * We can't call any function that calls PR_GetCurrentThread() * either (e.g., PR_SetError()) as that will result in infinite * recursion. */if(!_pr_initialized)returnNULL;/* PR_NEWZAP must not call PR_GetCurrentThread() */thred=PR_NEWZAP(PRThread);if(NULL!=thred){intrv;thred->priority=PR_PRIORITY_NORMAL;thred->id=pthread_self();thred->idSet=PR_TRUE;#ifdef _PR_NICE_PRIORITY_SCHEDULINGthred->tid=gettid();#endifrv=pthread_setspecific(pt_book.key,thred);PR_ASSERT(0==rv);thred->state=PT_THREAD_GLOBAL|PT_THREAD_FOREIGN;PR_Lock(pt_book.ml);/* then put it into the list */thred->prev=pt_book.last;if(pt_book.last)pt_book.last->next=thred;elsept_book.first=thred;thred->next=NULL;pt_book.last=thred;PR_Unlock(pt_book.ml);}returnthred;/* may be NULL */}/* pt_AttachThread */staticPRThread*_PR_CreateThread(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize,PRBoolisGCAble){intrv;PRThread*thred;pthread_attr_ttattr;if(!_pr_initialized)_PR_ImplicitInitialization();if((PRIntn)PR_PRIORITY_FIRST>(PRIntn)priority)priority=PR_PRIORITY_FIRST;elseif((PRIntn)PR_PRIORITY_LAST<(PRIntn)priority)priority=PR_PRIORITY_LAST;rv=_PT_PTHREAD_ATTR_INIT(&tattr);PR_ASSERT(0==rv);if(EPERM!=pt_schedpriv){#if !defined(_PR_DCETHREADS) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0structsched_paramschedule;#endif#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0rv=pthread_attr_setinheritsched(&tattr,PTHREAD_EXPLICIT_SCHED);PR_ASSERT(0==rv);#endif/* Use the default scheduling policy */#if defined(_PR_DCETHREADS)rv=pthread_attr_setprio(&tattr,pt_PriorityMap(priority));PR_ASSERT(0==rv);#elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0rv=pthread_attr_getschedparam(&tattr,&schedule);PR_ASSERT(0==rv);schedule.sched_priority=pt_PriorityMap(priority);rv=pthread_attr_setschedparam(&tattr,&schedule);PR_ASSERT(0==rv);#ifdef NTOrv=pthread_attr_setschedpolicy(&tattr,SCHED_RR);/* Round Robin */PR_ASSERT(0==rv);#endif#endif /* !defined(_PR_DCETHREADS) */}/* * DCE threads can't set detach state before creating the thread. * AIX can't set detach late. Why can't we all just get along? */#if !defined(_PR_DCETHREADS)rv=pthread_attr_setdetachstate(&tattr,((PR_JOINABLE_THREAD==state)?PTHREAD_CREATE_JOINABLE:PTHREAD_CREATE_DETACHED));PR_ASSERT(0==rv);#endif /* !defined(_PR_DCETHREADS) *//* * If stackSize is 0, we use the default pthread stack size. */if(stackSize){#ifdef _MD_MINIMUM_STACK_SIZEif(stackSize<_MD_MINIMUM_STACK_SIZE)stackSize=_MD_MINIMUM_STACK_SIZE;#endifrv=pthread_attr_setstacksize(&tattr,stackSize);PR_ASSERT(0==rv);}thred=PR_NEWZAP(PRThread);if(NULL==thred){PR_SetError(PR_OUT_OF_MEMORY_ERROR,errno);gotodone;}else{pthread_tid;thred->arg=arg;thred->startFunc=start;thred->priority=priority;if(PR_UNJOINABLE_THREAD==state)thred->state|=PT_THREAD_DETACHED;if(PR_LOCAL_THREAD==scope)scope=PR_GLOBAL_THREAD;if(PR_GLOBAL_BOUND_THREAD==scope){#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0rv=pthread_attr_setscope(&tattr,PTHREAD_SCOPE_SYSTEM);if(rv){/* * system scope not supported */scope=PR_GLOBAL_THREAD;/* * reset scope */rv=pthread_attr_setscope(&tattr,PTHREAD_SCOPE_PROCESS);PR_ASSERT(0==rv);}#endif}if(PR_GLOBAL_THREAD==scope)thred->state|=PT_THREAD_GLOBAL;elseif(PR_GLOBAL_BOUND_THREAD==scope)thred->state|=(PT_THREAD_GLOBAL|PT_THREAD_BOUND);else/* force it global */thred->state|=PT_THREAD_GLOBAL;if(PR_SYSTEM_THREAD==type)thred->state|=PT_THREAD_SYSTEM;thred->suspend=(isGCAble)?PT_THREAD_SETGCABLE:0;thred->stack=PR_NEWZAP(PRThreadStack);if(thred->stack==NULL){PRIntnoserr=errno;PR_Free(thred);/* all that work ... poof! */PR_SetError(PR_OUT_OF_MEMORY_ERROR,oserr);thred=NULL;/* and for what? */gotodone;}thred->stack->stackSize=stackSize;thred->stack->thr=thred;#ifdef PT_NO_SIGTIMEDWAITpthread_mutex_init(&thred->suspendResumeMutex,NULL);pthread_cond_init(&thred->suspendResumeCV,NULL);#endif/* make the thread counted to the rest of the runtime */PR_Lock(pt_book.ml);if(PR_SYSTEM_THREAD==type)pt_book.system+=1;elsept_book.user+=1;PR_Unlock(pt_book.ml);/* * We pass a pointer to a local copy (instead of thred->id) * to pthread_create() because who knows what wacky things * pthread_create() may be doing to its argument. */rv=_PT_PTHREAD_CREATE(&id,tattr,_pt_root,thred);#if !defined(_PR_DCETHREADS)if(EPERM==rv){#if defined(IRIX)if(PR_GLOBAL_BOUND_THREAD==scope){/* * SCOPE_SYSTEM requires appropriate privilege * reset to process scope and try again */rv=pthread_attr_setscope(&tattr,PTHREAD_SCOPE_PROCESS);PR_ASSERT(0==rv);thred->state&=~PT_THREAD_BOUND;}#else/* Remember that we don't have thread scheduling privilege. */pt_schedpriv=EPERM;PR_LOG(_pr_thread_lm,PR_LOG_MIN,("_PR_CreateThread: no thread scheduling privilege"));/* Try creating the thread again without setting priority. */#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0rv=pthread_attr_setinheritsched(&tattr,PTHREAD_INHERIT_SCHED);PR_ASSERT(0==rv);#endif#endif /* IRIX */rv=_PT_PTHREAD_CREATE(&id,tattr,_pt_root,thred);}#endifif(0!=rv){#if defined(_PR_DCETHREADS)PRIntnoserr=errno;#elsePRIntnoserr=rv;#endifPR_Lock(pt_book.ml);if(thred->state&PT_THREAD_SYSTEM)pt_book.system-=1;elseif(--pt_book.user==pt_book.this_many)PR_NotifyAllCondVar(pt_book.cv);PR_Unlock(pt_book.ml);PR_Free(thred->stack);PR_Free(thred);/* all that work ... poof! */PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR,oserr);thred=NULL;/* and for what? */gotodone;}PR_Lock(pt_book.ml);/* * Both the parent thread and this new thread set thred->id. * The parent thread must ensure that thred->id is set before * PR_CreateThread() returns. (See comments in _pt_root().) */if(!thred->idSet){thred->id=id;thred->idSet=PR_TRUE;}else{PR_ASSERT(pthread_equal(thred->id,id));}/* * If the new thread is detached, tell it that PR_CreateThread() has * accessed thred->id and thred->idSet so it's ok to delete thred. */if(PR_UNJOINABLE_THREAD==state){thred->okToDelete=PR_TRUE;PR_NotifyAllCondVar(pt_book.cv);}PR_Unlock(pt_book.ml);}done:rv=_PT_PTHREAD_ATTR_DESTROY(&tattr);PR_ASSERT(0==rv);returnthred;}/* _PR_CreateThread */PR_IMPLEMENT(PRThread*)PR_CreateThread(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize){return_PR_CreateThread(type,start,arg,priority,scope,state,stackSize,PR_FALSE);}/* PR_CreateThread */PR_IMPLEMENT(PRThread*)PR_CreateThreadGCAble(PRThreadTypetype,void(*start)(void*arg),void*arg,PRThreadPrioritypriority,PRThreadScopescope,PRThreadStatestate,PRUint32stackSize){return_PR_CreateThread(type,start,arg,priority,scope,state,stackSize,PR_TRUE);}/* PR_CreateThreadGCAble */PR_IMPLEMENT(void*)GetExecutionEnvironment(PRThread*thred){returnthred->environment;}/* GetExecutionEnvironment */PR_IMPLEMENT(void)SetExecutionEnvironment(PRThread*thred,void*env){thred->environment=env;}/* SetExecutionEnvironment */PR_IMPLEMENT(PRThread*)PR_AttachThread(PRThreadTypetype,PRThreadPrioritypriority,PRThreadStack*stack){returnPR_GetCurrentThread();}/* PR_AttachThread */PR_IMPLEMENT(PRStatus)PR_JoinThread(PRThread*thred){intrv=-1;void*result=NULL;PR_ASSERT(thred!=NULL);if((0xafafafaf==thred->state)||(PT_THREAD_DETACHED==(PT_THREAD_DETACHED&thred->state))||(PT_THREAD_FOREIGN==(PT_THREAD_FOREIGN&thred->state))){/* * This might be a bad address, but if it isn't, the state should * either be an unjoinable thread or it's already had the object * deleted. However, the client that called join on a detached * thread deserves all the rath I can muster.... */PR_SetError(PR_INVALID_ARGUMENT_ERROR,0);PR_LogPrint("PR_JoinThread: %p not joinable | already smashed\n",thred);}else{pthread_tid=thred->id;rv=pthread_join(id,&result);PR_ASSERT(rv==0&&result==NULL);if(0==rv){#ifdef _PR_DCETHREADSrv=pthread_detach(&id);PR_ASSERT(0==rv);#endif/* * PR_FALSE, because the thread already called the TPD * destructors before exiting _pt_root. */_pt_thread_death_internal(thred,PR_FALSE);}else{PRErrorCodeprerror;switch(rv){caseEINVAL:/* not a joinable thread */caseESRCH:/* no thread with given ID */prerror=PR_INVALID_ARGUMENT_ERROR;break;caseEDEADLK:/* a thread joining with itself */prerror=PR_DEADLOCK_ERROR;break;default:prerror=PR_UNKNOWN_ERROR;break;}PR_SetError(prerror,rv);}}return(0==rv)?PR_SUCCESS:PR_FAILURE;}/* PR_JoinThread */PR_IMPLEMENT(void)PR_DetachThread(void){void*thred;intrv;_PT_PTHREAD_GETSPECIFIC(pt_book.key,thred);if(NULL==thred)return;_pt_thread_death(thred);rv=pthread_setspecific(pt_book.key,NULL);PR_ASSERT(0==rv);}/* PR_DetachThread */PR_IMPLEMENT(PRThread*)PR_GetCurrentThread(void){void*thred;if(!_pr_initialized)_PR_ImplicitInitialization();_PT_PTHREAD_GETSPECIFIC(pt_book.key,thred);if(NULL==thred)thred=pt_AttachThread();PR_ASSERT(NULL!=thred);return(PRThread*)thred;}/* PR_GetCurrentThread */PR_IMPLEMENT(PRThreadScope)PR_GetThreadScope(constPRThread*thred){return(thred->state&PT_THREAD_BOUND)?PR_GLOBAL_BOUND_THREAD:PR_GLOBAL_THREAD;}/* PR_GetThreadScope() */PR_IMPLEMENT(PRThreadType)PR_GetThreadType(constPRThread*thred){return(thred->state&PT_THREAD_SYSTEM)?PR_SYSTEM_THREAD:PR_USER_THREAD;}PR_IMPLEMENT(PRThreadState)PR_GetThreadState(constPRThread*thred){return(thred->state&PT_THREAD_DETACHED)?PR_UNJOINABLE_THREAD:PR_JOINABLE_THREAD;}/* PR_GetThreadState */PR_IMPLEMENT(PRThreadPriority)PR_GetThreadPriority(constPRThread*thred){PR_ASSERT(thred!=NULL);returnthred->priority;}/* PR_GetThreadPriority */PR_IMPLEMENT(void)PR_SetThreadPriority(PRThread*thred,PRThreadPrioritynewPri){PRIntnrv;PR_ASSERT(NULL!=thred);if((PRIntn)PR_PRIORITY_FIRST>(PRIntn)newPri)newPri=PR_PRIORITY_FIRST;elseif((PRIntn)PR_PRIORITY_LAST<(PRIntn)newPri)newPri=PR_PRIORITY_LAST;#if defined(_PR_DCETHREADS)rv=pthread_setprio(thred->id,pt_PriorityMap(newPri));/* pthread_setprio returns the old priority */#elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0if(EPERM!=pt_schedpriv){intpolicy;structsched_paramschedule;rv=pthread_getschedparam(thred->id,&policy,&schedule);if(0==rv){schedule.sched_priority=pt_PriorityMap(newPri);rv=pthread_setschedparam(thred->id,policy,&schedule);if(EPERM==rv){pt_schedpriv=EPERM;PR_LOG(_pr_thread_lm,PR_LOG_MIN,("PR_SetThreadPriority: no thread scheduling privilege"));}}if(rv!=0)rv=-1;}#elif defined(_PR_NICE_PRIORITY_SCHEDULING)PR_Lock(pt_book.ml);while(thred->tid==0)PR_WaitCondVar(pt_book.cv,PR_INTERVAL_NO_TIMEOUT);PR_Unlock(pt_book.ml);errno=0;rv=getpriority(PRIO_PROCESS,0);/* Do not proceed unless we know the main thread's nice value. */if(errno==0){rv=setpriority(PRIO_PROCESS,thred->tid,pt_RelativePriority(rv,newPri));if(rv==-1){/* We don't set pt_schedpriv to EPERM in case errno == EPERM * because adjusting the nice value might be permitted for certain * ranges but not for others. */PR_LOG(_pr_thread_lm,PR_LOG_MIN,("PR_SetThreadPriority: setpriority failed with error %d",errno));}}#else(void)rv;/* rv is unused */#endifthred->priority=newPri;}/* PR_SetThreadPriority */PR_IMPLEMENT(PRStatus)PR_Interrupt(PRThread*thred){/* ** If the target thread indicates that it's waiting, ** find the condition and broadcast to it. Broadcast ** since we don't know which thread (if there are more ** than one). This sounds risky, but clients must ** test their invariants when resumed from a wait and ** I don't expect very many threads to be waiting on ** a single condition and I don't expect interrupt to ** be used very often. ** ** I don't know why I thought this would work. Must have ** been one of those weaker momements after I'd been ** smelling the vapors. ** ** Even with the followng changes it is possible that ** the pointer to the condition variable is pointing ** at a bogus value. Will the unerlying code detect ** that? */PRCondVar*cv;PR_ASSERT(NULL!=thred);if(NULL==thred)returnPR_FAILURE;thred->state|=PT_THREAD_ABORTED;cv=thred->waiting;if((NULL!=cv)&&!thred->interrupt_blocked){PRIntnrv;(void)PR_ATOMIC_INCREMENT(&cv->notify_pending);rv=pthread_cond_broadcast(&cv->cv);PR_ASSERT(0==rv);if(0>PR_ATOMIC_DECREMENT(&cv->notify_pending))PR_DestroyCondVar(cv);}returnPR_SUCCESS;}/* PR_Interrupt */PR_IMPLEMENT(void)PR_ClearInterrupt(void){PRThread*me=PR_GetCurrentThread();me->state&=~PT_THREAD_ABORTED;}/* PR_ClearInterrupt */PR_IMPLEMENT(void)PR_BlockInterrupt(void){PRThread*me=PR_GetCurrentThread();_PT_THREAD_BLOCK_INTERRUPT(me);}/* PR_BlockInterrupt */PR_IMPLEMENT(void)PR_UnblockInterrupt(void){PRThread*me=PR_GetCurrentThread();_PT_THREAD_UNBLOCK_INTERRUPT(me);}/* PR_UnblockInterrupt */PR_IMPLEMENT(PRStatus)PR_Yield(void){staticPRBoolwarning=PR_TRUE;if(warning)warning=_PR_Obsolete("PR_Yield()","PR_Sleep(PR_INTERVAL_NO_WAIT)");returnPR_Sleep(PR_INTERVAL_NO_WAIT);}PR_IMPLEMENT(PRStatus)PR_Sleep(PRIntervalTimeticks){PRStatusrv=PR_SUCCESS;if(!_pr_initialized)_PR_ImplicitInitialization();if(PR_INTERVAL_NO_WAIT==ticks){_PT_PTHREAD_YIELD();}else{PRCondVar*cv;PRIntervalTimetimein;timein=PR_IntervalNow();cv=PR_NewCondVar(_pr_sleeplock);PR_ASSERT(cv!=NULL);PR_Lock(_pr_sleeplock);do{PRIntervalTimenow=PR_IntervalNow();PRIntervalTimedelta=now-timein;if(delta>ticks)break;rv=PR_WaitCondVar(cv,ticks-delta);}while(PR_SUCCESS==rv);PR_Unlock(_pr_sleeplock);PR_DestroyCondVar(cv);}returnrv;}/* PR_Sleep */staticvoid_pt_thread_death(void*arg){void*thred;intrv;_PT_PTHREAD_GETSPECIFIC(pt_book.key,thred);if(NULL==thred){/* * Have PR_GetCurrentThread return the expected value to the * destructors. */rv=pthread_setspecific(pt_book.key,arg);PR_ASSERT(0==rv);}/* PR_TRUE for: call destructors */_pt_thread_death_internal(arg,PR_TRUE);if(NULL==thred){rv=pthread_setspecific(pt_book.key,NULL);PR_ASSERT(0==rv);}}staticvoid_pt_thread_death_internal(void*arg,PRBoolcallDestructors){PRThread*thred=(PRThread*)arg;if(thred->state&(PT_THREAD_FOREIGN|PT_THREAD_PRIMORD)){PR_Lock(pt_book.ml);if(NULL==thred->prev)pt_book.first=thred->next;elsethred->prev->next=thred->next;if(NULL==thred->next)pt_book.last=thred->prev;elsethred->next->prev=thred->prev;PR_Unlock(pt_book.ml);}if(callDestructors)_PR_DestroyThreadPrivate(thred);PR_Free(thred->privateData);if(NULL!=thred->errorString)PR_Free(thred->errorString);if(NULL!=thred->name)PR_Free(thred->name);PR_Free(thred->stack);if(NULL!=thred->syspoll_list)PR_Free(thred->syspoll_list);#if defined(_PR_POLL_WITH_SELECT)if(NULL!=thred->selectfd_list)PR_Free(thred->selectfd_list);#endif#if defined(DEBUG)memset(thred,0xaf,sizeof(PRThread));#endif /* defined(DEBUG) */PR_Free(thred);}/* _pt_thread_death */void_PR_InitThreads(PRThreadTypetype,PRThreadPrioritypriority,PRUintnmaxPTDs){intrv;PRThread*thred;PR_ASSERT(priority==PR_PRIORITY_NORMAL);#ifdef _PR_NEED_PTHREAD_INIT/* * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily * initialized, but pthread_self() fails to initialize * pthreads and hence returns a null thread ID if invoked * by the primordial thread before any other pthread call. * So we explicitly initialize pthreads here. */pthread_init();#endif#if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0#if defined(FREEBSD){pthread_attr_tattr;intpolicy;/* get the min and max priorities of the default policy */pthread_attr_init(&attr);pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);pthread_attr_getschedpolicy(&attr,&policy);pt_book.minPrio=sched_get_priority_min(policy);PR_ASSERT(-1!=pt_book.minPrio);pt_book.maxPrio=sched_get_priority_max(policy);PR_ASSERT(-1!=pt_book.maxPrio);pthread_attr_destroy(&attr);}#else/* ** These might be function evaluations */pt_book.minPrio=PT_PRIO_MIN;pt_book.maxPrio=PT_PRIO_MAX;#endif#endifPR_ASSERT(NULL==pt_book.ml);pt_book.ml=PR_NewLock();PR_ASSERT(NULL!=pt_book.ml);pt_book.cv=PR_NewCondVar(pt_book.ml);PR_ASSERT(NULL!=pt_book.cv);thred=PR_NEWZAP(PRThread);PR_ASSERT(NULL!=thred);thred->arg=NULL;thred->startFunc=NULL;thred->priority=priority;thred->id=pthread_self();thred->idSet=PR_TRUE;#ifdef _PR_NICE_PRIORITY_SCHEDULINGthred->tid=gettid();#endifthred->state=(PT_THREAD_DETACHED|PT_THREAD_PRIMORD);if(PR_SYSTEM_THREAD==type){thred->state|=PT_THREAD_SYSTEM;pt_book.system+=1;pt_book.this_many=0;}else{pt_book.user+=1;pt_book.this_many=1;}thred->next=thred->prev=NULL;pt_book.first=pt_book.last=thred;thred->stack=PR_NEWZAP(PRThreadStack);PR_ASSERT(thred->stack!=NULL);thred->stack->stackSize=0;thred->stack->thr=thred;_PR_InitializeStack(thred->stack);/* * Create a key for our use to store a backpointer in the pthread * to our PRThread object. This object gets deleted when the thread * returns from its root in the case of a detached thread. Other * threads delete the objects in Join. * * NB: The destructor logic seems to have a bug so it isn't used. * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998. * More info - the problem is that pthreads calls the destructor * eagerly as the thread returns from its root, rather than lazily * after the thread is joined. Therefore, threads that are joining * and holding PRThread references are actually holding pointers to * nothing. */rv=_PT_PTHREAD_KEY_CREATE(&pt_book.key,_pt_thread_death);if(0!=rv)PR_Assert("0 == rv",__FILE__,__LINE__);pt_book.keyCreated=PR_TRUE;rv=pthread_setspecific(pt_book.key,thred);PR_ASSERT(0==rv);}/* _PR_InitThreads */#ifdef __GNUC__/* * GCC supports the constructor and destructor attributes as of * version 2.5. */staticvoid_PR_Fini(void)__attribute__((destructor));#elif defined(__SUNPRO_C)/* * Sun Studio compiler */#pragma fini(_PR_Fini)staticvoid_PR_Fini(void);#elif defined(HPUX)/* * Current versions of HP C compiler define __HP_cc. * HP C compiler A.11.01.20 doesn't define __HP_cc. */#if defined(__ia64) || defined(_LP64)#pragma FINI "_PR_Fini"staticvoid_PR_Fini(void);#else/* * Only HP-UX 10.x style initializers are supported in 32-bit links. * Need to use the +I PR_HPUX10xInit linker option. */#include<dl.h>staticvoid_PR_Fini(void);voidPR_HPUX10xInit(shl_thandle,intloading){/* * This function is called when a shared library is loaded as well * as when the shared library is unloaded. Note that it may not * be called when the user's program terminates. * * handle is the shl_load API handle for the shared library being * initialized. * * loading is non-zero at startup and zero at termination. */if(loading){/* ... do some initializations ... */}else{_PR_Fini();}}#endif#elif defined(AIX)/* Need to use the -binitfini::_PR_Fini linker option. */#endifvoid_PR_Fini(void){void*thred;intrv;if(!_pr_initialized){/* Either NSPR was never successfully initialized or * PR_Cleanup has been called already. */if(pt_book.keyCreated){rv=pthread_key_delete(pt_book.key);PR_ASSERT(0==rv);pt_book.keyCreated=PR_FALSE;}return;}_PT_PTHREAD_GETSPECIFIC(pt_book.key,thred);if(NULL!=thred){/* * PR_FALSE, because it is unsafe to call back to the * thread private data destructors at final cleanup. */_pt_thread_death_internal(thred,PR_FALSE);rv=pthread_setspecific(pt_book.key,NULL);PR_ASSERT(0==rv);}rv=pthread_key_delete(pt_book.key);PR_ASSERT(0==rv);pt_book.keyCreated=PR_FALSE;/* TODO: free other resources used by NSPR *//* _pr_initialized = PR_FALSE; */}/* _PR_Fini */PR_IMPLEMENT(PRStatus)PR_Cleanup(void){PRThread*me=PR_GetCurrentThread();intrv;PR_LOG(_pr_thread_lm,PR_LOG_MIN,("PR_Cleanup: shutting down NSPR"));PR_ASSERT(me->state&PT_THREAD_PRIMORD);if(me->state&PT_THREAD_PRIMORD){PR_Lock(pt_book.ml);while(pt_book.user>pt_book.this_many)PR_WaitCondVar(pt_book.cv,PR_INTERVAL_NO_TIMEOUT);if(me->state&PT_THREAD_SYSTEM)pt_book.system-=1;elsept_book.user-=1;PR_Unlock(pt_book.ml);_PR_MD_EARLY_CLEANUP();_PR_CleanupMW();_PR_CleanupTime();_PR_CleanupDtoa();_PR_CleanupCallOnce();_PR_ShutdownLinker();_PR_LogCleanup();_PR_CleanupNet();/* Close all the fd's before calling _PR_CleanupIO */_PR_CleanupIO();_PR_CleanupCMon();_pt_thread_death(me);rv=pthread_setspecific(pt_book.key,NULL);PR_ASSERT(0==rv);/* * I am not sure if it's safe to delete the cv and lock here, * since there may still be "system" threads around. If this * call isn't immediately prior to exiting, then there's a * problem. */if(0==pt_book.system){PR_DestroyCondVar(pt_book.cv);pt_book.cv=NULL;PR_DestroyLock(pt_book.ml);pt_book.ml=NULL;}PR_DestroyLock(_pr_sleeplock);_pr_sleeplock=NULL;_PR_CleanupLayerCache();_PR_CleanupEnv();#ifdef _PR_ZONE_ALLOCATOR_PR_DestroyZones();#endif_pr_initialized=PR_FALSE;returnPR_SUCCESS;}returnPR_FAILURE;}/* PR_Cleanup */PR_IMPLEMENT(void)PR_ProcessExit(PRIntnstatus){_exit(status);}PR_IMPLEMENT(PRUint32)PR_GetThreadID(PRThread*thred){#if defined(_PR_DCETHREADS)return(PRUint32)&thred->id;/* this is really a sham! */#elsereturn(PRUint32)thred->id;/* and I don't know what they will do with it */#endif}/* * $$$ * The following two thread-to-processor affinity functions are not * yet implemented for pthreads. By the way, these functions should return * PRStatus rather than PRInt32 to indicate the success/failure status. * $$$ */PR_IMPLEMENT(PRInt32)PR_GetThreadAffinityMask(PRThread*thread,PRUint32*mask){return0;/* not implemented */}PR_IMPLEMENT(PRInt32)PR_SetThreadAffinityMask(PRThread*thread,PRUint32mask){return0;/* not implemented */}PR_IMPLEMENT(void)PR_SetThreadDumpProc(PRThread*thread,PRThreadDumpProcdump,void*arg){thread->dump=dump;thread->dumpArg=arg;}/* * Garbage collection support follows. */#if defined(_PR_DCETHREADS)/* * statics for Garbage Collection support. We don't need to protect these * signal masks since the garbage collector itself is protected by a lock * and multiple threads will not be garbage collecting at the same time. */staticsigset_tjavagc_vtalarm_sigmask;staticsigset_tjavagc_intsoff_sigmask;#else /* defined(_PR_DCETHREADS) *//* a bogus signal mask for forcing a timed wait *//* Not so bogus in AIX as we really do a sigwait */staticsigset_tsigwait_set;staticstructtimespeconemillisec={0,1000000L};#ifndef PT_NO_SIGTIMEDWAITstaticstructtimespechundredmillisec={0,100000000L};#endifstaticvoidsuspend_signal_handler(PRIntnsig);#ifdef PT_NO_SIGTIMEDWAITstaticvoidnull_signal_handler(PRIntnsig);#endif#endif /* defined(_PR_DCETHREADS) *//* * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which * conflict with the use of these two signals in our GC support. * So we don't know how to support GC on Linux pthreads. */staticvoidinit_pthread_gc_support(void){#ifndef SYMBIANPRIntnrv;#if defined(_PR_DCETHREADS)rv=sigemptyset(&javagc_vtalarm_sigmask);PR_ASSERT(0==rv);rv=sigaddset(&javagc_vtalarm_sigmask,SIGVTALRM);PR_ASSERT(0==rv);#else /* defined(_PR_DCETHREADS) */{structsigactionsigact_usr2;sigact_usr2.sa_handler=suspend_signal_handler;sigact_usr2.sa_flags=SA_RESTART;sigemptyset(&sigact_usr2.sa_mask);rv=sigaction(SIGUSR2,&sigact_usr2,NULL);PR_ASSERT(0==rv);sigemptyset(&sigwait_set);#if defined(PT_NO_SIGTIMEDWAIT)sigaddset(&sigwait_set,SIGUSR1);#elsesigaddset(&sigwait_set,SIGUSR2);#endif /* defined(PT_NO_SIGTIMEDWAIT) */}#if defined(PT_NO_SIGTIMEDWAIT){structsigactionsigact_null;sigact_null.sa_handler=null_signal_handler;sigact_null.sa_flags=SA_RESTART;sigemptyset(&sigact_null.sa_mask);rv=sigaction(SIGUSR1,&sigact_null,NULL);PR_ASSERT(0==rv);}#endif /* defined(PT_NO_SIGTIMEDWAIT) */#endif /* defined(_PR_DCETHREADS) */#endif /* SYMBIAN */}PR_IMPLEMENT(void)PR_SetThreadGCAble(void){PR_Lock(pt_book.ml);PR_GetCurrentThread()->state|=PT_THREAD_GCABLE;PR_Unlock(pt_book.ml);}PR_IMPLEMENT(void)PR_ClearThreadGCAble(void){PR_Lock(pt_book.ml);PR_GetCurrentThread()->state&=(~PT_THREAD_GCABLE);PR_Unlock(pt_book.ml);}#if defined(DEBUG)staticPRBoolsuspendAllOn=PR_FALSE;#endifstaticPRBoolsuspendAllSuspended=PR_FALSE;PR_IMPLEMENT(PRStatus)PR_EnumerateThreads(PREnumeratorfunc,void*arg){PRIntncount=0;PRStatusrv=PR_SUCCESS;PRThread*thred=pt_book.first;#if defined(DEBUG) || defined(FORCE_PR_ASSERT)#if !defined(_PR_DCETHREADS)PRThread*me=PR_GetCurrentThread();#endif#endifPR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_EnumerateThreads\n"));/* * $$$ * Need to suspend all threads other than me before doing this. * This is really a gross and disgusting thing to do. The only * good thing is that since all other threads are suspended, holding * the lock during a callback seems like child's play. * $$$ */PR_ASSERT(suspendAllOn);while(thred!=NULL){/* Steve Morse, 4-23-97: Note that we can't walk a queue by taking * qp->next after applying the function "func". In particular, "func" * might remove the thread from the queue and put it into another one in * which case qp->next no longer points to the next entry in the original * queue. * * To get around this problem, we save qp->next in qp_next before applying * "func" and use that saved value as the next value after applying "func". */PRThread*next=thred->next;if(_PT_IS_GCABLE_THREAD(thred)){#if !defined(_PR_DCETHREADS)PR_ASSERT((thred==me)||(thred->suspend&PT_THREAD_SUSPENDED));#endifPR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("In PR_EnumerateThreads callback thread %p thid = %X\n",thred,thred->id));rv=func(thred,count++,arg);if(rv!=PR_SUCCESS)returnrv;}thred=next;}PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End PR_EnumerateThreads count = %d \n",count));returnrv;}/* PR_EnumerateThreads *//* * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend. * The signal handler will record the stack pointer and will block until resumed by * the resume call. Since the signal handler is the last routine called for the * suspended thread, the stack pointer will also serve as a place where all the * registers have been saved on the stack for the previously executing routines. * * Through global variables, we also make sure that PR_Suspend and PR_Resume does not * proceed until the thread is suspended or resumed. */#if !defined(_PR_DCETHREADS)/* * In the signal handler, we can not use condition variable notify or wait. * This does not work consistently across all pthread platforms. We also can not * use locking since that does not seem to work reliably across platforms. * Only thing we can do is yielding while testing for a global condition * to change. This does work on pthread supported platforms. We may have * to play with priortities if there are any problems detected. *//* * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java, * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal * handler as all synchronization mechanisms just break down. */#if defined(PT_NO_SIGTIMEDWAIT)staticvoidnull_signal_handler(PRIntnsig){return;}#endifstaticvoidsuspend_signal_handler(PRIntnsig){PRThread*me=PR_GetCurrentThread();PR_ASSERT(me!=NULL);PR_ASSERT(_PT_IS_GCABLE_THREAD(me));PR_ASSERT((me->suspend&PT_THREAD_SUSPENDED)==0);PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin suspend_signal_handler thred %p thread id = %X\n",me,me->id));/* * save stack pointer */me->sp=&me;/* At this point, the thread's stack pointer has been saved, And it is going to enter a wait loop until it is resumed. So it is _really_ suspended */me->suspend|=PT_THREAD_SUSPENDED;/* * now, block current thread */#if defined(PT_NO_SIGTIMEDWAIT)pthread_cond_signal(&me->suspendResumeCV);while(me->suspend&PT_THREAD_SUSPENDED){#if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \ && !defined(BSDI) && !defined(UNIXWARE) \ && !defined(DARWIN) && !defined(RISCOS) \ && !defined(SYMBIAN) /*XXX*/PRIntnrv;sigwait(&sigwait_set,&rv);#endif}me->suspend|=PT_THREAD_RESUMED;pthread_cond_signal(&me->suspendResumeCV);#else /* defined(PT_NO_SIGTIMEDWAIT) */while(me->suspend&PT_THREAD_SUSPENDED){PRIntnrv=sigtimedwait(&sigwait_set,NULL,&hundredmillisec);PR_ASSERT(-1==rv);}me->suspend|=PT_THREAD_RESUMED;#endif/* * At this point, thread has been resumed, so set a global condition. * The ResumeAll needs to know that this has really been resumed. * So the signal handler sets a flag which PR_ResumeAll will reset. * The PR_ResumeAll must reset this flag ... */PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End suspend_signal_handler thred = %p tid = %X\n",me,me->id));}/* suspend_signal_handler */staticvoidpt_SuspendSet(PRThread*thred){PRIntnrv;PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("pt_SuspendSet thred %p thread id = %X\n",thred,thred->id));/* * Check the thread state and signal the thread to suspend */PR_ASSERT((thred->suspend&PT_THREAD_SUSPENDED)==0);PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n",thred,thred->id));#if defined(SYMBIAN)/* All signal group functions are not implemented in Symbian OS */rv=0;#elserv=pthread_kill(thred->id,SIGUSR2);#endifPR_ASSERT(0==rv);}staticvoidpt_SuspendTest(PRThread*thred){PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin pt_SuspendTest thred %p thread id = %X\n",thred,thred->id));/* * Wait for the thread to be really suspended. This happens when the * suspend signal handler stores the stack pointer and sets the state * to suspended. */#if defined(PT_NO_SIGTIMEDWAIT)pthread_mutex_lock(&thred->suspendResumeMutex);while((thred->suspend&PT_THREAD_SUSPENDED)==0){pthread_cond_timedwait(&thred->suspendResumeCV,&thred->suspendResumeMutex,&onemillisec);}pthread_mutex_unlock(&thred->suspendResumeMutex);#elsewhile((thred->suspend&PT_THREAD_SUSPENDED)==0){PRIntnrv=sigtimedwait(&sigwait_set,NULL,&onemillisec);PR_ASSERT(-1==rv);}#endifPR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End pt_SuspendTest thred %p tid %X\n",thred,thred->id));}/* pt_SuspendTest */staticvoidpt_ResumeSet(PRThread*thred){PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("pt_ResumeSet thred %p thread id = %X\n",thred,thred->id));/* * Clear the global state and set the thread state so that it will * continue past yield loop in the suspend signal handler */PR_ASSERT(thred->suspend&PT_THREAD_SUSPENDED);thred->suspend&=~PT_THREAD_SUSPENDED;#if defined(PT_NO_SIGTIMEDWAIT)#if defined(SYMBIAN) /* All signal group functions are not implemented in Symbian OS */#elsepthread_kill(thred->id,SIGUSR1);#endif#endif}/* pt_ResumeSet */staticvoidpt_ResumeTest(PRThread*thred){PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin pt_ResumeTest thred %p thread id = %X\n",thred,thred->id));/* * Wait for the threads resume state to change * to indicate it is really resumed */#if defined(PT_NO_SIGTIMEDWAIT)pthread_mutex_lock(&thred->suspendResumeMutex);while((thred->suspend&PT_THREAD_RESUMED)==0){pthread_cond_timedwait(&thred->suspendResumeCV,&thred->suspendResumeMutex,&onemillisec);}pthread_mutex_unlock(&thred->suspendResumeMutex);#elsewhile((thred->suspend&PT_THREAD_RESUMED)==0){PRIntnrv=sigtimedwait(&sigwait_set,NULL,&onemillisec);PR_ASSERT(-1==rv);}#endifthred->suspend&=~PT_THREAD_RESUMED;PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End pt_ResumeTest thred %p tid %X\n",thred,thred->id));}/* pt_ResumeTest */staticpthread_once_tpt_gc_support_control=PTHREAD_ONCE_INIT;PR_IMPLEMENT(void)PR_SuspendAll(void){#ifdef DEBUGPRIntervalTimestime,etime;#endifPRThread*thred=pt_book.first;PRThread*me=PR_GetCurrentThread();intrv;rv=pthread_once(&pt_gc_support_control,init_pthread_gc_support);PR_ASSERT(0==rv);PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_SuspendAll\n"));/* * Stop all threads which are marked GC able. */PR_Lock(pt_book.ml);#ifdef DEBUGsuspendAllOn=PR_TRUE;stime=PR_IntervalNow();#endifwhile(thred!=NULL){if((thred!=me)&&_PT_IS_GCABLE_THREAD(thred))pt_SuspendSet(thred);thred=thred->next;}/* Wait till they are really suspended */thred=pt_book.first;while(thred!=NULL){if((thred!=me)&&_PT_IS_GCABLE_THREAD(thred))pt_SuspendTest(thred);thred=thred->next;}suspendAllSuspended=PR_TRUE;#ifdef DEBUGetime=PR_IntervalNow();PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,\("End PR_SuspendAll (time %dms)\n",PR_IntervalToMilliseconds(etime-stime)));#endif}/* PR_SuspendAll */PR_IMPLEMENT(void)PR_ResumeAll(void){#ifdef DEBUGPRIntervalTimestime,etime;#endifPRThread*thred=pt_book.first;PRThread*me=PR_GetCurrentThread();PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_ResumeAll\n"));/* * Resume all previously suspended GC able threads. */suspendAllSuspended=PR_FALSE;#ifdef DEBUGstime=PR_IntervalNow();#endifwhile(thred!=NULL){if((thred!=me)&&_PT_IS_GCABLE_THREAD(thred))pt_ResumeSet(thred);thred=thred->next;}thred=pt_book.first;while(thred!=NULL){if((thred!=me)&&_PT_IS_GCABLE_THREAD(thred))pt_ResumeTest(thred);thred=thred->next;}PR_Unlock(pt_book.ml);#ifdef DEBUGsuspendAllOn=PR_FALSE;etime=PR_IntervalNow();PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End PR_ResumeAll (time %dms)\n",PR_IntervalToMilliseconds(etime-stime)));#endif}/* PR_ResumeAll *//* Return the stack pointer for the given thread- used by the GC */PR_IMPLEMENT(void*)PR_GetSP(PRThread*thred){PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("in PR_GetSP thred %p thid = %X, sp = %p\n",thred,thred->id,thred->sp));returnthred->sp;}/* PR_GetSP */#else /* !defined(_PR_DCETHREADS) */staticpthread_once_tpt_gc_support_control=pthread_once_init;/* * For DCE threads, there is no pthread_kill or a way of suspending or resuming a * particular thread. We will just disable the preemption (virtual timer alarm) and * let the executing thread finish the garbage collection. This stops all other threads * (GC able or not) and is very inefficient but there is no other choice. */PR_IMPLEMENT(void)PR_SuspendAll(){PRIntnrv;rv=pthread_once(&pt_gc_support_control,init_pthread_gc_support);PR_ASSERT(0==rv);/* returns -1 on failure */#ifdef DEBUGsuspendAllOn=PR_TRUE;#endifPR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_SuspendAll\n"));/* * turn off preemption - i.e add virtual alarm signal to the set of * blocking signals */rv=sigprocmask(SIG_BLOCK,&javagc_vtalarm_sigmask,&javagc_intsoff_sigmask);PR_ASSERT(0==rv);suspendAllSuspended=PR_TRUE;PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End PR_SuspendAll\n"));}/* PR_SuspendAll */PR_IMPLEMENT(void)PR_ResumeAll(){PRIntnrv;suspendAllSuspended=PR_FALSE;PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_ResumeAll\n"));/* turn on preemption - i.e re-enable virtual alarm signal */rv=sigprocmask(SIG_SETMASK,&javagc_intsoff_sigmask,(sigset_t*)NULL);PR_ASSERT(0==rv);#ifdef DEBUGsuspendAllOn=PR_FALSE;#endifPR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End PR_ResumeAll\n"));}/* PR_ResumeAll *//* Return the stack pointer for the given thread- used by the GC */PR_IMPLEMENT(void*)PR_GetSP(PRThread*thred){pthread_ttid=thred->id;char*thread_tcb,*top_sp;/* * For HPUX DCE threads, pthread_t is a struct with the * following three fields (see pthread.h, dce/cma.h): * cma_t_address field1; * short int field2; * short int field3; * where cma_t_address is typedef'd to be either void* * or char*. */PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("Begin PR_GetSP\n"));thread_tcb=(char*)tid.field1;top_sp=*(char**)(thread_tcb+128);PR_LOG(_pr_gc_lm,PR_LOG_ALWAYS,("End PR_GetSP %p \n",top_sp));returntop_sp;}/* PR_GetSP */#endif /* !defined(_PR_DCETHREADS) */PR_IMPLEMENT(PRStatus)PR_SetCurrentThreadName(constchar*name){PRThread*thread;size_tnameLen;intresult=0;if(!name){PR_SetError(PR_INVALID_ARGUMENT_ERROR,0);returnPR_FAILURE;}thread=PR_GetCurrentThread();if(!thread)returnPR_FAILURE;PR_Free(thread->name);nameLen=strlen(name);thread->name=(char*)PR_Malloc(nameLen+1);if(!thread->name)returnPR_FAILURE;memcpy(thread->name,name,nameLen+1);#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)pthread_set_name_np(thread->id,name);#elif defined(NETBSD)result=pthread_setname_np(thread->id,"%s",(void*)name);#else /* not BSD *//* * On OSX, pthread_setname_np is only available in 10.6 or later, so test * for it at runtime. It also may not be available on all linux distros. */#if defined(DARWIN)int(*dynamic_pthread_setname_np)(constchar*);#elseint(*dynamic_pthread_setname_np)(pthread_t,constchar*);#endif*(void**)(&dynamic_pthread_setname_np)=dlsym(RTLD_DEFAULT,"pthread_setname_np");if(!dynamic_pthread_setname_np)returnPR_SUCCESS;/* * The 15-character name length limit is an experimentally determined * length of a null-terminated string that most linux distros and OS X * accept as an argument to pthread_setname_np. Otherwise the E2BIG * error is returned by the function. */#define SETNAME_LENGTH_CONSTRAINT 15#define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)#define SETNAME_FRAGMENT2_LENGTH \ (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)charname_dup[SETNAME_LENGTH_CONSTRAINT+1];if(nameLen>SETNAME_LENGTH_CONSTRAINT){memcpy(name_dup,name,SETNAME_FRAGMENT1_LENGTH);name_dup[SETNAME_FRAGMENT1_LENGTH]='~';/* Note that this also copies the null terminator. */memcpy(name_dup+SETNAME_FRAGMENT1_LENGTH+1,name+nameLen-SETNAME_FRAGMENT2_LENGTH,SETNAME_FRAGMENT2_LENGTH+1);name=name_dup;}#if defined(DARWIN)result=dynamic_pthread_setname_np(name);#elseresult=dynamic_pthread_setname_np(thread->id,name);#endif#endif /* not BSD */if(result){PR_SetError(PR_UNKNOWN_ERROR,result);returnPR_FAILURE;}returnPR_SUCCESS;}PR_IMPLEMENT(constchar*)PR_GetThreadName(constPRThread*thread){if(!thread)returnNULL;returnthread->name;}#endif /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) *//* ptthread.c */